﻿package com.dcliment.utils
{
	import flash.display.Shape;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.getTimer;

	/**
	 * Custom Timer Class :: Allows easy place to start stop pause and overall manage Timers
	 * Creates the timer using the bulit in flash getTimer() function instead of the usual Timer Class
	 * 
	 * Would love to make a Timer Management Class but that's for another day...
	 * 
	 * NOTE 11/11/2013 :: Callback funtion after the if statement that checks repeatCount
	 *  
	 * @author dcliment
	 * version 1.2
	 * 
	 */	
	public class CustomTimer extends EventDispatcher
	{
		public static var TIMER:String = "timer";
		
		private var _state:String = "Off";
		private var _callback:Function;
		private var _running:Boolean = false;
		
		private var _startTime		:int = 0;
		private var _elapsedTime	:int = 0;
		private var _pauseTime		:int = 0;
		private var _restartTime	:int = 0;
		private var _lastingTime	:int = 0;
		private var _delay			:int = 0;
		
		private var _currentCount	:uint = 0;
		private var _repeatCount	:uint = 0;
		
		private var EventHolder:Shape;
		
		/**
		 * Creates a precise Timer
		 * 
		 * @param	$timerDelay  	:: Timer Delay in milliseconds
		 * @param	$callback 		:: Function to run after timer finished
		 * @param   $repeatCount 	:: Times to repeat the Timer, if repeatCount is 0 it will repeat infinitely unless you pause it, default: 1
		 * @param	$startTimer 	:: If true Timer will start immediately after Timer is created, default: false
		 */
		public function CustomTimer($timerDelay:int, $callback:Function, $repeatCount:uint = 1, $startTimer:Boolean = false):void
		{
			_delay 			= $timerDelay;
			_callback 		= $callback;
			_repeatCount 	= $repeatCount;
			
			EventHolder = new Shape();
			EventHolder.name = "EventHolder";
			
			if ($startTimer)
			{
				start();
			}
		}
		
		private function onEvent(e:Event):void
		{
			// THese are Fail-Safes
			if (!_running)
			{
				//trace("CustomTimer.onEvent() :: FIRST if statement?");
				return;
			}
			if (_repeatCount != 0)
			{
				if (_currentCount == _repeatCount)
				{
					//trace("CustomTimer.onEvent() :: SECOND  if statemnet?");
					return;
				}
			}
			
			
			_elapsedTime = (getTimer() - _restartTime + _pauseTime) - _startTime;
			_lastingTime = _delay - _elapsedTime;
			
			
			if (_lastingTime <= 0)
			{
				//trace("are you in the 3rd if statement?");
			
				_currentCount++;
				if (_repeatCount != 0)
				{
					if (_currentCount == _repeatCount)
					{
						_state = "Finished";
						destroy();
					}
					else
					{
						start();
					}
				}
				else
				{
					start();
				}
				
				//----------------
				// CAllBack function fired 
				// NOTE :: this needs to be after the if else statement above (might find issues if the timerDelay time is small though
				if (_callback != null)
				{
					_callback();
				}
			}
			
			
			
			dispatchEvent(new Event(CustomTimer.TIMER));
		}
		
		/**
		 * Starts Timer
		 */
		public function start():void
		{
			if (!EventHolder.hasEventListener("enterFrame"))
			{
				create();
				//trace("CustomTimer.start() :: NO EnterFrame event found so created one ::");
			}
			switch (_state)
			{
				case "Finished": 
				case "Off": 
				case "On": 
					_startTime = getTimer();
					_pauseTime = 0;
					_restartTime = 0;
					_elapsedTime = 0;
					_lastingTime = 0;
					_state = "On";
					_running = true;
					break;
				
				case "Paused": 
					_restartTime = getTimer();
					_state = "On";
					_running = true;
					break;
				
				default: 
					throw new Error("Undefined State: " + _state);
			}
		}
		
		/**
		 * Resets Timer
		 */
		public function reset():void
		{
			_startTime = 0;
			_pauseTime = 0;
			_restartTime = 0;
			_elapsedTime = 0;
			_lastingTime = 0;
			_state = "Off";
			_currentCount = 0;
			_running = false;
			
			//trace("Customtimer.reset() firing? ");
		}
		
		/**
		 * Pauses Timer, use start function to unpause
		 */
		public function pause():void
		{
			_pauseTime = getTimer() - _restartTime + _pauseTime;
			_state = "Paused";
			_running = false;
		}
		public function stop():void
		{
			_state = "Off";
			_running = false;
			
			destroy();
			
		}
		/**
		 * Restart the timer, resets time and starts it all in one function;
		 */		
		public function restart():void
		{
			this.reset();
			this.start();
		}
		/**
		 * Creates Timer :: don't remmeber why thi sis public...
		 */
		public function create():void
		{
			if (!EventHolder.hasEventListener("enterFrame"))
			{
				EventHolder.addEventListener("enterFrame", onEvent);
			}
		}
		
		/**
		 * Destroyes Timer, you can create it again using create function
		 */
		public function destroy():void
		{
			if (EventHolder.hasEventListener("enterFrame"))
			{
				EventHolder.removeEventListener("enterFrame", onEvent);
				reset();
			}
		}
		
		/**
		 * Gets Seconds elapsed from start in float number (1.1, 7.534...)
		 * @param	toInt converts float seconds in integer (1, 8...)
		 */
		public function getSeconds(toInt:Boolean = false):Number
		{
			if (toInt)
			{
				return parseInt(Math.floor(elapsedTime / 1000) + "." + elapsedTime % 1000);
			}
			return parseFloat(Math.floor(elapsedTime / 1000) + "." + elapsedTime % 1000);
		}
		
		/**
		 * Timer's state (On, Off, Paused, Finished), this property is read-only
		 */
		public function get state():String
		{
			return _state;
		}
		
		/**
		 * Function to run after timer finished
		 */
		public function set callback(val:Function):void
		{
			_callback = val;
		}
		
		/**
		 * Function to run after timer finished
		 */
		public function get callback():Function
		{
			return _callback;
		}
		
		/**
		 * true if Timer is running, false if not
		 */
		public function set running(val:Boolean):void
		{
			_running = val;
		}
		
		/**
		 * true if Timer is running, false if not
		 */
		public function get running():Boolean
		{
			return _running;
		}
		
		/**
		 * Elapsed time from start, this property is read-only
		 */
		public function get elapsedTime():int
		{
			return _elapsedTime;
		}
		
		/**
		 * Lasting time until finish, this property is read-only
		 */
		public function get lastingTime():int
		{
			return _lastingTime;
		}
		
		/**
		 * Timer Delay in milliseconds
		 */
		public function set delay(val:int):void
		{
			_delay = val;
		}
		
		/**
		 * Timer Delay in milliseconds
		 */
		public function get delay():int
		{
			return _delay;
		}
		
		/**
		 * Total Times Timer has finished since it started
		 */
		public function set currentCount(val:uint)
		{
			_currentCount = val;
		}
		
		/**
		 * Total Times Timer has finished since it started
		 */
		public function get currentCount():uint
		{
			return _currentCount;
		}
		
		/**
		 * Times to repeat the Timer, if repeatCount is 0 it will repeat infinitely unless you pause it
		 */
		public function set repeatCount(val:uint):void
		{
			_repeatCount = val;
		}
		
		/**
		 * Times to repeat the Timer, if repeatCount is 0 it will repeat infinitely unless you pause it
		 */
		public function get repeatCount():uint
		{
			return _repeatCount;
		}
	
	}
}